﻿using System;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Xml.Linq;

namespace VirusCleaner.Update
{
    public class Updater
    {
        #region 事件订阅

        public event Action<long, long, int> StateChanged;
        #endregion
        #region 成员变量


        private string _address;
        private readonly string _softName;
        private readonly string _filePath;
        private readonly Version _currVer;
        public string UpdateUrl { get; set; }

        private System.Threading.Timer _timer;
        #endregion
        #region 构造函数
        public Updater(string loadFile)
        {
            Assembly assembly = Assembly.LoadFile(loadFile);
            _softName = assembly.GetName().Name;
            _currVer = assembly.GetName().Version;
            _filePath = System.IO.Path.GetDirectoryName(loadFile);
        }
        public Updater(string softName,string version)
        {
            this._softName = softName;
            //
            this._currVer = new Version(version);
            //
            this._filePath = GetType().Assembly.Location;
        }
        #endregion
        #region 公开方法
        public bool Update()
        {
            if (CheckUpdate() == false)
                return false;
            bool retValue = false;
            //
            System.IO.Stream st = null,so = null;
            try
            {
                
                var by = new byte[1024];
                _timer = new System.Threading.Timer(new System.Threading.TimerCallback(TimerCallback), null,0, 1000);
                var webRequest = (HttpWebRequest)WebRequest.Create(_address);
                long totalBytes;
                long totalDownloadedByte;
                HttpWebResponse webResponse;
                using (webResponse = (HttpWebResponse) webRequest.GetResponse())
                {
                    totalBytes = webResponse.ContentLength;
                    totalDownloadedByte = 0;
                    st = webResponse.GetResponseStream();
                }
                string fileName = System.IO.Path.GetFileName(_address);
                if (fileName != null)
                    so = new System.IO.FileStream(System.IO.Path.Combine(_filePath,fileName), System.IO.FileMode.Create);
                if (st != null)
                {
                    int osize = st.Read(by, 0, (int)by.Length);
                    while (osize > 0)
                    {
                        totalDownloadedByte = osize + totalDownloadedByte;
                        if (so != null) so.Write(by, 0, osize);
                        osize = st.Read(by, 0, (int)by.Length);
                        if(StateChanged != null)
                        {
                            _i = totalDownloadedByte;
                            StateChanged(totalDownloadedByte, totalBytes, (int) _k);
                        }
                    }
                }
                if (so != null) so.Flush();
                if (st != null) st.Flush();
                _timer.Change(0,0);
                retValue = totalDownloadedByte.Equals(totalBytes);
            }
            catch(Exception ex)
            {
                throw new Exception(ex.Message);
            }
            finally
            {
                if (so != null)
                    so.Close();
                if (st != null)
                    st.Close();
            }

            return retValue;
        }

        private long _i, _j, _k;
        private void TimerCallback(object obj)
        {
            _k = _i - _j;
            _j = _i;
        }

        public bool CheckUpdate()
        {
            bool retValue = false;

            Version version = GetVersion();

            retValue = _currVer < version;

            return retValue;
        }

        public Version GetVersion()
        {
            var version = new Version();
            //
            try
            {
                XDocument xdoc = XDocument.Load(UpdateUrl);
                XElement xroot = xdoc.Element("Update");
                var query = from soft in xroot.Descendants("Soft")
                            select new
                                       {
                                           Version = soft.Element("Version").Value,
                                           Address = soft.Element("Address").Value,
                                           AppName = soft.Attribute("AppName").Value,
                                       };

                foreach (var item in query)
                {
                    if (item.AppName.Equals(_softName,StringComparison.OrdinalIgnoreCase))
                    {
                        _address = item.Address.ToString();
                        version = new Version(item.Version);
                    }
                }
            }
                catch(NullReferenceException)
            {
            
                
                }
        catch(Exception ex)
            {
                throw new Exception(ex.Message);
            }
            return version;
        }
        #endregion
        #region 重写方法
        public override string ToString()
        {
            return this._softName;
        }
        #endregion
    }
}
